package top.lilele.adminSystem.service;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.ShearCaptcha;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.lilele.adminSystem.dao.UserListInfoDao;
import top.lilele.adminSystem.dto.*;
import top.lilele.adminSystem.entity.AdminDepartmentEntity;
import top.lilele.adminSystem.entity.AdminUserDepartmentEntity;
import top.lilele.adminSystem.entity.AdminUserEntity;
import top.lilele.adminSystem.entity.AdminUserRoleEntity;
import top.lilele.adminSystem.exception.account.AccountDisableException;
import top.lilele.adminSystem.exception.account.AccountWrongException;
import top.lilele.adminSystem.exception.account.PasswordErrorException;
import top.lilele.adminSystem.handler.WebSocketHandler;
import top.lilele.adminSystem.mapper.AdminDepartmentMapper;
import top.lilele.adminSystem.mapper.AdminUserDepartmentMapper;
import top.lilele.adminSystem.mapper.AdminUserMapper;
import top.lilele.adminSystem.mapper.AdminUserRoleMapper;
import top.lilele.adminSystem.utils.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author lilele
 */
@Service
@Slf4j
public class AdminUserService {
    @Resource
    AdminUserMapper adminUserMapper;

    @Resource
    AdminUserRoleMapper adminUserRoleMapper;

    @Resource
    AdminUserDepartmentMapper adminUserDepartmentMapper;

    @Resource
    AdminDepartmentMapper adminDepartmentMapper;

    @Resource
    AsyncOperate asyncOperate;

    @Resource
    RedisUtil redisUtil;

    @Value("${account.salt}")
    String salt;

    @Value("${account.defaultPassword}")
    String defaultPassword;

    /**
     * 登录
     *
     * @param loginDto
     * @return
     */
    @SneakyThrows
    public String login(LoginDto loginDto) {
        LambdaQueryWrapper<AdminUserEntity> wrapper = new LambdaQueryWrapper<AdminUserEntity>().eq(AdminUserEntity::getUsername, loginDto.getUsername()).eq(AdminUserEntity::getPassword, DigestUtil.set(loginDto.getPassword() + salt, "SHA-1"));
        AdminUserEntity info = adminUserMapper.selectOne(wrapper);
        log.info("查询用户信息 {}", info);
        Optional.ofNullable(info).orElseThrow(() -> new AccountWrongException("账号密码错误"));
        if (info.getAvailable() == 2) {
            throw new AccountDisableException("账号已被冻结");
        }
        // 是否已经登录
        String token = StpUtil.getTokenValueByLoginId(info.getId());
        if (StringUtils.isNotEmpty(token)) {
            BaseResponse exceptionInfo = BaseResponse.info(ResponseCodeEnum.BeSqueezedDown, null);
            WebSocketHandler.sendMessage(token, exceptionInfo);
        }
        StpUtil.login(info.getId());
        redisUtil.del(loginDto.getUid());
        return StpUtil.getTokenInfo().getTokenValue();
    }

    /**
     * 更新用户
     *
     * @param addUserDto
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateUserInfoById(AddUserDto addUserDto) {
        AdminUserEntity adminUserEntity = new AdminUserEntity();
        // 排除密码、账号权限
        BeanUtils.copyProperties(addUserDto, adminUserEntity, Utils.getName(AddUserDto::getPassword), Utils.getName(AddUserDto::getIsAdmin));
        adminUserMapper.updateById(adminUserEntity);
        // 清空用户的角色列表
        adminUserRoleMapper.delete(new LambdaQueryWrapper<AdminUserRoleEntity>().eq(AdminUserRoleEntity::getUserId, addUserDto.getId()));
        AdminUserRoleEntity adminUserRoleEntity = new AdminUserRoleEntity();
        adminUserRoleEntity.setUserId(addUserDto.getId());
        // 用户-角色关联
        if (Objects.nonNull(addUserDto.getRoleList())) {
            addUserDto.getRoleList().forEach(item -> {
                adminUserRoleEntity.setRoleId(item);
                adminUserRoleMapper.insert(adminUserRoleEntity);
            });
        }
    }


    @Transactional(rollbackFor = Exception.class)
    @SneakyThrows
    public void addUser(AddUserDto addUserDto) {
        // 加密密码
        String finalPassword = DigestUtil.set(StringUtils.isEmpty(addUserDto.getPassword()) ? defaultPassword : addUserDto.getPassword() + salt, "SHA-1");
        AdminUserEntity adminUserEntity = new AdminUserEntity();
        // 拷贝bean，过滤角色列表
        BeanUtils.copyProperties(addUserDto, adminUserEntity, Utils.getName(AddUserDto::getRoleList));
        adminUserEntity.setPassword(finalPassword);
        // 录入用户基本信息
        adminUserEntity.setIsAdmin(2);
        adminUserMapper.insert(adminUserEntity);
        AdminUserRoleEntity adminUserRoleEntity = new AdminUserRoleEntity();
        // 用户-角色关联信息
        adminUserRoleEntity.setUserId(adminUserEntity.getId());
        if (Objects.nonNull(addUserDto.getRoleList())) {
            addUserDto.getRoleList().forEach(item -> {
                adminUserRoleEntity.setRoleId(item);
                adminUserRoleMapper.insert(adminUserRoleEntity);
            });
        }
        // 发送邮件
        asyncOperate.sendAddUserMail(addUserDto);
    }

    /**
     * 查询用户列表
     *
     * @param userListInfoDao
     * @return
     */
    public Object queryUserList(UserListInfoDao userListInfoDao, Boolean isPage) {
        List<UserListDto> list = adminUserMapper.queryUserList();
        list = list.stream().filter(item -> {
            boolean isTrue = true;
            // 姓名搜索
            if (StringUtils.isNotEmpty(userListInfoDao.getTrueName()) && !item.getTrueName().contains(userListInfoDao.getTrueName())) {
                isTrue = false;
            }
            // 账号状态搜索
            if (userListInfoDao.getAvailable() != null && !Objects.equals(item.getAvailable(), userListInfoDao.getAvailable())) {
                isTrue = false;
            }
            return isTrue;
        }).sorted(Comparator.comparing(UserListDto::getTrueName)).collect(Collectors.toList());
        // 拼接list集合
        if (isPage) {
            return Utils.generatePage(list, userListInfoDao.getPageNum(), userListInfoDao.getPageSize());
        } else {
            return list;
        }
    }

    /**
     * 根据id删除用户
     *
     * @param id
     */
    @Transactional(rollbackFor = Exception.class)
    public void deleteUserById(String id) {
        // 删除用户信息
        adminUserMapper.deleteById(id);
        // 删除用户-角色关联信息
        adminUserRoleMapper.delete(new LambdaQueryWrapper<AdminUserRoleEntity>().eq(AdminUserRoleEntity::getUserId, id));
        // 删除用户-部门关联信息
        adminUserDepartmentMapper.delete(new LambdaQueryWrapper<AdminUserDepartmentEntity>().eq(AdminUserDepartmentEntity::getUserId, id));
        // 退出登录
        StpUtil.logout(id);
    }

    /**
     * 根据用户id查询用户信息与角色
     *
     * @param id
     * @return
     */
    public UserListDto queryUserInfoById(String id) {
        // 用户基本信息
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("au.id", id);
        UserListDto userListDto = adminUserMapper.queryUserInfoAndRoleById(queryWrapper);
        return userListDto;
    }

    /**
     * 根据用户id获取用户信息
     *
     * @param userId
     * @return
     */
    public UserListDto queryUserById(String userId) {
        UserListDto userInfo = adminUserMapper.queryUserById(userId);
        return userInfo;
    }

    /**
     * 修改密码
     *
     * @param changePasswordDto
     * @return
     */
    @SneakyThrows
    public void changePassword(ChangePasswordDto changePasswordDto) {
        // 原始密码校验
        String originPwd = DigestUtil.set(changePasswordDto.getOriginPassword() + salt, "SHA-1");
        LambdaQueryWrapper<AdminUserEntity> wrapper = new LambdaQueryWrapper<AdminUserEntity>().eq(AdminUserEntity::getPassword, originPwd).eq(AdminUserEntity::getId, StpUtil.getLoginIdAsString());
        AdminUserEntity info = adminUserMapper.selectOne(wrapper);
        if (info == null) {
            throw new PasswordErrorException();
        }
        // 修改密码
        String finalPwd = DigestUtil.set(changePasswordDto.getFinalPassword() + salt, "SHA-1");
        AdminUserEntity adminUserEntity = new AdminUserEntity();
        adminUserEntity.setId(StpUtil.getLoginIdAsString());
        adminUserEntity.setPassword(finalPwd);
        adminUserMapper.updateById(adminUserEntity);
        // 退出登录
        StpUtil.logout();
    }

    @SneakyThrows
    public void export(UserListInfoDao userListInfoDao, HttpServletResponse httpServletResponse) {
        String fileName = "用户信息-" + DateUtil.format(DateUtil.date(), "yyyy-MM-dd-HH-mm-ss");
        List list = (List) this.queryUserList(userListInfoDao, false);
        LinkedHashMap<Object, Object> map = Maps.newLinkedHashMap();
        map.put(Utils.getName(UserListDto::getTrueName), "用户名");
        map.put(Utils.getName(UserListDto::getUsername), "账号");
        map.put(Utils.getName(UserListDto::getPhone), "手机号");
        map.put(Utils.getName(UserListDto::getEmail), "邮箱");
        map.put(Utils.getName(UserListDto::getInductionTime), "入职时间");
        Utils.exportExcel(list, fileName, "用户报表", map, httpServletResponse);
    }


    public List<DeptUserDao> selectAllDeptAndUser() {
        // 获取所有部门
        List<AdminDepartmentEntity> adminDepartmentEntities = adminDepartmentMapper.selectList(new LambdaQueryWrapper<AdminDepartmentEntity>().orderByAsc(AdminDepartmentEntity::getSort));
        // 获取部门对应的成员
        List<DeptUser> deptUserDaoList = adminUserDepartmentMapper.selectDeptUser();
        // 获取没有部门的成员
        List<DeptUser> noDeptUserList = adminUserDepartmentMapper.selectNoDeptUser();
        // 部门数据转换
        List<DeptUserDao> deptList = adminDepartmentEntities.stream().map(item -> {
            DeptUserDao deptUserDao = new DeptUserDao();
            deptUserDao.setName(item.getDepartmentName());
            deptUserDao.setType(2);
            deptUserDao.setId(item.getId());
            deptUserDao.setParentId(item.getParentId());
            return deptUserDao;
        }).collect(Collectors.toList());
        // 拼接部门-用户数据
        List<DeptUserDao> finalList = deptList.stream().map(item -> {
            // 部门下的用户列表
            List<DeptUserDao> userList = deptUserDaoList.stream().filter(item0 -> item0.getDeptId().equals(item.getId())).map(item0 -> {
                DeptUserDao deptUserDao = new DeptUserDao();
                deptUserDao.setName(item0.getName());
                deptUserDao.setType(1);
                deptUserDao.setId(item0.getUserId());
                deptUserDao.setParentId(item.getId());
                return deptUserDao;
            }).sorted(Comparator.comparing(DeptUserDao::getName)).collect(Collectors.toList());
            item.setChildren(userList);
            return item;
        }).collect(Collectors.toList());
        // 递归节点
        Stream<DeptUserDao> deptUserDaoStream = finalList.stream().filter(m -> m.getParentId().equals("0")).map(
                (m) -> {
                    ArrayList<String> pathList = Lists.newArrayList();
                    pathList.add(m.getId());
                    m.setPath(pathList);
                    // 合并部门-用户
                    List<DeptUserDao> collect = Stream.concat(setChild(m, finalList, pathList).stream(), m.getChildren().stream()).collect(Collectors.toList());
                    m.setChildren(collect);
                    return m;
                }
        );
        Stream<DeptUserDao> noDeptUserStream = noDeptUserList.stream().map(item -> {
            DeptUserDao deptUserDao = new DeptUserDao();
            deptUserDao.setParentId("0");
            deptUserDao.setChildren(new ArrayList<>());
            deptUserDao.setId(item.getUserId());
            deptUserDao.setType(1);
            deptUserDao.setName(item.getName());
            return deptUserDao;
        });
        List<DeptUserDao> list = Stream.concat(deptUserDaoStream, noDeptUserStream).collect(Collectors.toList());
        return list;
    }

    private List<DeptUserDao> setChild(DeptUserDao node, List<DeptUserDao> list, List<String> path) {
        List<DeptUserDao> children = list.stream().filter(m -> Objects.equals(m.getParentId(), node.getId())).map(
                (m) -> {
                    ArrayList<String> pathList = Lists.newArrayList(m.getId());
                    m.setPath(pathList);
                    List<String> pathFinal = Stream.concat(path.stream(), pathList.stream()).collect(Collectors.toList());
                    m.setPath(pathFinal);
                    List<DeptUserDao> collect = Stream.concat(setChild(m, list, pathFinal).stream(), m.getChildren().stream()).collect(Collectors.toList());
                    m.setChildren(collect);
                    return m;
                }
        ).collect(Collectors.toList());
        return children;
    }

    @SneakyThrows
    public void generateCode(HttpServletResponse httpServletResponse) {
        String uid = IdUtil.simpleUUID();
        ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(90, 36, 4, 4);
        httpServletResponse.setCharacterEncoding("utf-8");
        httpServletResponse.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(uid, "UTF-8"));
        redisUtil.set(uid, captcha.getCode(), 60 * 5);
        captcha.write(httpServletResponse.getOutputStream());
    }
}
